<?php
namespace Ffan\Eagle;

class Eagle
{
    const TAG_DEFAULT_VALUE     = 'undefined';
    const DATA_DEFAULT_VALUE    = '0';

    private $path           = '/var/wd/wrs/logs/eagle/';
    private $exceptionPath  = '/var/wd/wrs/logs/eagle_error/';
    public $params = [
        'magic'=>'abdae13d',    // 业务服务器编号
        'version'=>'v1',        // 版本号
        'aggregate'=>'',        // 需要非聚合查询的话, 传'false'
        '',
        'timestamp'=>'',        // 时间戳,精确到毫秒
        'traceID'=>'',
        'appName'=>'other',     // 系统名称
        'IP'=>'',               // IP
        'measurement'=>'',      // 业务名称
        'data'=>[],             // 数据字段
        'tag'=>[]               // 数据标签, 一般是枚举类型, 不要把用户ID等一些维度数据存在此处
    ];

    public static $instance = null;

    /**
     * Eagle constructor.
     * @param $appName 项目名称
     */
    public function __construct($appName)
    {
        if(!empty($appName)){
            $this->params['appName'] = $appName;
        }
    }

    public static function getInstance($appName){
        if(empty(self::$instance[$appName])){
            self::$instance[$appName] = new Eagle($appName);
        }

        return self::$instance[$appName];
    }

    /**
     * 写入埋点日志
     * @param $measurement 业务类型
     * @param $data 数据字段
     * @param $tag 数据标签
     * @param $aggregate 'false':可以根据{$data}进行聚合查询, 录入UV  '': 无法实现{$data}参数的聚合查询
     */
    public function write($measurement, $data, $tag, $aggregate=''){

        try{
            if($aggregate===false){
                $aggregate = 'false';
            }
            $this->params['aggregate'] = $aggregate;
            $this->params['timestamp'] = $this->getTimestamp();

            $this->params['IP'] = $this->getIp();
            $this->params['measurement'] = $measurement;

            if(empty($data)){
                EagleException::getException(EagleException::DATA_ERROR_ARRAY_EMPTY);
            }

            if($this->arrayValuesISEmpty($data)){
                EagleException::getException(EagleException::DATA_ERROR_VALUES_ALL_EMPTY);
            }

            if(empty($measurement)){
                EagleException::getException(EagleException::MEASUREMENT_IS_EMPTY);
            }

            if(!(empty($aggregate) || $aggregate===false || $aggregate==='false')){
                EagleException::getException(EagleException::AGGREGATE_VALUE_ERROR);
            }

            $this->params['data'] = $this->dataToString($this->toSaveData($data, $aggregate));
            $this->params['tag'] = $this->dataToString($this->toSaveTag($tag));
            $log = implode('|', $this->params) . "|\n";

            $this->saveLog($this->path, $log);

            if($this->validValueHasEmpty($data)){
                EagleException::getException(EagleException::DATA_ERROR_VALUES_HAS_EMPTY);
            }

            if($this->validValueHasEmpty($tag)){
                EagleException::getException(EagleException::TAG_ERROR_VALUES_HAS_EMPTY);
            }
        } catch (\Exception $e){
            if(!isset($log)){
                $log = false;
            }
            $msg = $e->getMessage().",measurement:{$measurement}:[data:".json_encode($data).",tag:".json_encode($tag)."aggregate:{$aggregate}]\n";
            $this->saveLog($this->exceptionPath, $msg);
        }

        return $log;
    }


    /**
     * 设置日志路径
     * @param $path
     */
    public function setLogPath($path){
        if(!empty($path)){
            $this->path = $path;
        }
    }

    /**
     * 设置异常日志路径
     * @param $path
     */
    public function setExceptionLogPath($path){
        if(!empty($path)){
            $this->exceptionPath = $path;
        }
    }

    private function saveLog($path, $msg){
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $date = date('Y-m-d');

        $fp = fopen($path . "/eagle-{$date}.log", "a+");

        flock($fp, LOCK_EX);
        fwrite($fp, $msg);
        flock($fp, LOCK_UN);
        fclose($fp);
    }

    /**
     * 检查data数据是否合法
     * @param $data
     * @param $aggregate
     * @return mixed
     */
    private function toSaveData($data, $aggregate){

        foreach ($data as $k=>$v){
            if($data[$k]==''){
                $data[$k] = self::DATA_DEFAULT_VALUE;
            }

            if($aggregate =='' && !is_numeric($v)){
                $data[$k] = self::DATA_DEFAULT_VALUE;
            }
        }

        return $data;
    }

    /**
     * 检查tag数据是否合法
     * @param $tag
     * @return mixed
     */
    private function toSaveTag($tag){
        foreach ($tag as $k=>$v){
            if($v == ''){
                $tag[$k] = self::TAG_DEFAULT_VALUE;
            }
        }

        return $tag;
    }

    private function validValueHasEmpty($data){

        foreach ($data as $v){
            if($v == ''){
                return true;
            }
        }

        return false;
    }

    /**
     * 检查数组值是否全部是空
     * @param $data
     * @return bool
     */
    private function arrayValuesISEmpty($data){
        $value_empty_times = 0;
        $isEmpty = false;
        foreach ($data as $k=>$v){
            if($v === ''){
                $value_empty_times++;
            }
        }

        if(count($data) == $value_empty_times){
            $isEmpty =  true;
        }

        return $isEmpty;
    }

    private function getIp(){
        if(PHP_SAPI=='cli'){
            return '127.0.0.1';
        }

        if (isset($_SERVER)) {
            if($_SERVER['SERVER_ADDR']) {
                $server_ip = $_SERVER['SERVER_ADDR'];
            } else {
                $server_ip = $_SERVER['LOCAL_ADDR'];
            }
        } else {
            $server_ip = getenv('SERVER_ADDR');
        }
        return $server_ip;
    }

    private function dataToString($data){
        $dataTmp = [];

        foreach ($data as $k=>$v){
            $dataTmp[] = "{$k}={$v}";
        }

        $string = implode(',', $dataTmp);

        return $string;
    }

    private function getTimestamp(){
        return round(microtime(true)*1000);
    }
}